{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d006b2ea-9dfe-49c7-88a9-a5a0775185fd",
   "metadata": {},
   "source": [
    "# Additional End of week Exercise - week 2\n",
    "\n",
    "Now use everything you've learned from Week 2 to build a full prototype for the technical question/answerer you built in Week 1 Exercise.\n",
    "\n",
    "This should include a Gradio UI, streaming, use of the system prompt to add expertise, and the ability to switch between models. Bonus points if you can demonstrate use of a tool!\n",
    "\n",
    "If you feel bold, see if you can add audio input so you can talk to it, and have it respond with audio. ChatGPT or Claude can help you, or email me if you have questions.\n",
    "\n",
    "I will publish a full solution here soon - unless someone beats me to it...\n",
    "\n",
    "There are so many commercial applications for this, from a language tutor, to a company onboarding solution, to a companion AI to a course (like this one!) I can't wait to see your results."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b82bbbd7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Imports and Setup\n",
    "import os\n",
    "import json\n",
    "import base64\n",
    "from io import BytesIO\n",
    "from dotenv import load_dotenv\n",
    "from openai import OpenAI\n",
    "import gradio as gr\n",
    "from PIL import Image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b7375979",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load environment variables\n",
    "load_dotenv(override=True)\n",
    "\n",
    "# API Keys\n",
    "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
    "anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n",
    "google_api_key = os.getenv('GOOGLE_API_KEY')\n",
    "\n",
    "if openai_api_key:\n",
    "    print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
    "else:\n",
    "    print(\"OpenAI API Key not set\")\n",
    "    \n",
    "if anthropic_api_key:\n",
    "    print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n",
    "else:\n",
    "    print(\"Anthropic API Key not set\")\n",
    "\n",
    "if google_api_key:\n",
    "    print(f\"Google API Key exists and begins {google_api_key[:8]}\")\n",
    "else:\n",
    "    print(\"Google API Key not set\")\n",
    "\n",
    "# Initialize clients\n",
    "anthropic_url = \"https://api.anthropic.com/v1/\"\n",
    "gemini_url = \"https://generativelanguage.googleapis.com/v1beta/openai/\"\n",
    "\n",
    "openai = OpenAI()\n",
    "anthropic = OpenAI(api_key=anthropic_api_key, base_url=anthropic_url)\n",
    "gemini = OpenAI(api_key=google_api_key, base_url=gemini_url)\n",
    "\n",
    "\n",
    "# Model constants\n",
    "MODELS = {\n",
    "    \"GPT-5\": \"gpt-5-mini\",\n",
    "    \"Claude Sonnet\": \"claude-sonnet-4-5-20250929\", \n",
    "    \"Gemini\": \"gemini-2.5-flash\"\n",
    "}\n",
    "\n",
    "print(\"All API clients initialized successfully!\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "634e3186",
   "metadata": {},
   "outputs": [],
   "source": [
    "def main_claude(messages, tools):\n",
    "\n",
    "    response = anthropic.chat.completions.create(\n",
    "        model=MODELS[\"Claude Sonnet\"],\n",
    "        messages=messages,\n",
    "        tools=tools\n",
    "    )\n",
    "    \n",
    "    return response\n",
    "\n",
    "def main_gpt(messages, tools):\n",
    "\n",
    "    response = openai.chat.completions.create(\n",
    "        model=MODELS[\"GPT-5\"],\n",
    "        messages=messages,\n",
    "        tools=tools\n",
    "    )\n",
    "    \n",
    "    return response\n",
    "\n",
    "def main_gemini(messages, tools):\n",
    "\n",
    "    response = gemini.chat.completions.create(\n",
    "        model=MODELS[\"Gemini\"],\n",
    "        messages=messages,\n",
    "        tools=tools\n",
    "    )\n",
    "    \n",
    "    return response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4b26fa50",
   "metadata": {},
   "outputs": [],
   "source": [
    "def main_model(messages, model, tools):\n",
    "    if model==\"GPT-5\":\n",
    "        result = main_gpt(messages, tools)\n",
    "    elif model==\"Claude Sonnet\":\n",
    "        result = main_claude(messages, tools)\n",
    "    elif model==\"Gemini\":\n",
    "        result = main_gemini(messages, tools)        \n",
    "    else:\n",
    "        raise ValueError(\"Unknown model\")\n",
    "    \n",
    "    return result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d8a08091",
   "metadata": {},
   "outputs": [],
   "source": [
    "initial_system_prompt = \"\"\"You are an individual that possesses a unique\n",
    "and highly valuable combination of deep technical expertise and \n",
    "excellent communication skills. You grasp complex, specialized \n",
    "concepts and then distill them into simple, understandable terms \n",
    "for people without the same technical background.\n",
    "\n",
    "IMPORTANT: When users ask for code examples or programming concepts, \n",
    "you MUST use the generate_code_example tool to provide working code examples. \n",
    "Do not write code directly in your response - always call the tool first.\n",
    "\n",
    "Present your answer as markdown and use the tool to generate working code examples \n",
    "when relevant to help illustrate the concepts.\"\"\"\n",
    "\n",
    "# Different system prompt for after tool calls\n",
    "followup_system_prompt = \"\"\"You are an individual that possesses a unique\n",
    "and highly valuable combination of deep technical expertise and \n",
    "excellent communication skills. You grasp complex, specialized \n",
    "concepts and then distill them into simple, understandable terms \n",
    "for people without the same technical background.\n",
    "\n",
    "IMPORTANT: You have already called the generate_code_example tool and received code. \n",
    "Now you should use that generated code in your response. Do NOT call the tool again.\n",
    "Present your answer as markdown and incorporate the provided code examples \n",
    "to help illustrate the concepts.\"\"\"\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0e0c9c82",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Code Example Generator Tool\n",
    "def generate_code_example(language=\"python\"):\n",
    "\n",
    "    print(f\"TOOL CALLED: Generating {language} code\")\n",
    "    \n",
    "    return f\"# Code example in {language}\\n# This is a placeholder - implement your specific example here\\n')\"\n",
    "\n",
    "# Tool definition\n",
    "code_tool = {\n",
    "    \"name\": \"generate_code_example\",\n",
    "    \"description\": \"Generate a working code example\",\n",
    "    \"parameters\": {\n",
    "        \"type\": \"object\",\n",
    "        \"properties\": {\n",
    "            \"language\": {\n",
    "                \"type\": \"string\", \n",
    "                \"description\": \"The programming language (default: python)\",\n",
    "                \"default\": \"python\"\n",
    "            }\n",
    "        },\n",
    "        \"additionalProperties\": False\n",
    "    }\n",
    "}\n",
    "\n",
    "tools = [{\"type\": \"function\", \"function\": code_tool}]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7a81370d",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# Audio and Image Generation Functions\n",
    "def transcribe_audio(audio_file):\n",
    "    \"\"\"Convert audio to text using OpenAI Whisper\"\"\"\n",
    "    if audio_file is None:\n",
    "        return None\n",
    "    \n",
    "    with open(audio_file, \"rb\") as audio:\n",
    "        transcript = openai.audio.transcriptions.create(\n",
    "            model=\"whisper-1\",\n",
    "            file=audio\n",
    "        )\n",
    "    return transcript.text\n",
    "\n",
    "def text_to_speech(text):\n",
    "    \"\"\"Convert text to speech using OpenAI TTS\"\"\"\n",
    "    response = openai.audio.speech.create(\n",
    "        model=\"gpt-4o-mini-tts\",\n",
    "        voice=\"coral\",\n",
    "        input=text\n",
    "    )\n",
    "    return response.content\n",
    "\n",
    "def generate_robot_image(code_snippet):\n",
    "    \"\"\"Generate an image of a robot developer at a monitor showing the code\"\"\"\n",
    "    prompt = f\"\"\"A robot developer sitting at a computer monitor in a modern office. Robot is styled\n",
    "    as Bender from Futurama.\n",
    "    The monitor displays code on the screen showing: {code_snippet[:100]}...\n",
    "    The robot has a friendly, helpful appearance with glowing eyes and mechanical hands typing.\n",
    "    The scene is well-lit with a professional coding environment background.\"\"\"\n",
    "    \n",
    "    response = openai.images.generate(\n",
    "        model=\"dall-e-3\",\n",
    "        prompt=prompt,\n",
    "        size=\"1024x1024\",\n",
    "        n=1,\n",
    "        response_format=\"b64_json\"\n",
    "    )\n",
    "    \n",
    "    image_base64 = response.data[0].b64_json\n",
    "    image_data = base64.b64decode(image_base64)\n",
    "    return Image.open(BytesIO(image_data))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "25c5279f",
   "metadata": {},
   "outputs": [],
   "source": [
    "def handle_tool_calls(message):\n",
    "    responses = []\n",
    "    generated_code = None\n",
    "    \n",
    "    for tool_call in message.tool_calls:\n",
    "        if tool_call.function.name == \"generate_code_example\":\n",
    "            arguments = json.loads(tool_call.function.arguments)\n",
    "            language = arguments.get('language', 'python')\n",
    "            code_example = generate_code_example(language)\n",
    "            generated_code = code_example\n",
    "            \n",
    "            responses.append({\n",
    "                \"role\": \"tool\",\n",
    "                \"content\": code_example,\n",
    "                \"tool_call_id\": tool_call.id\n",
    "            })\n",
    "    \n",
    "    return responses, generated_code\n",
    "\n",
    "def chat(message, history, model_choice):\n",
    "    history = [{\"role\": h[\"role\"], \"content\": h[\"content\"]} for h in history]\n",
    "    messages = [{\"role\": \"system\", \"content\": initial_system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n",
    "    \n",
    "    generated_code = None\n",
    "    image = None\n",
    "    \n",
    "    response = main_model(\n",
    "        model=model_choice,\n",
    "        messages=messages,\n",
    "        tools=tools\n",
    "    )\n",
    "\n",
    "    print(f\"Response finish_reason: {response.choices[0].finish_reason}\")\n",
    "    print(f\"Response tool_calls: {response.choices[0].message.tool_calls}\")\n",
    "\n",
    "    while response.choices[0].finish_reason == \"tool_calls\":\n",
    "        message = response.choices[0].message\n",
    "        responses, code = handle_tool_calls(message)\n",
    "        generated_code = code\n",
    "        messages.append(message)\n",
    "        messages.extend(responses)\n",
    "        response = main_model(\n",
    "            model=model_choice,\n",
    "            messages=messages,\n",
    "            tools=tools\n",
    "        )\n",
    "        messages[0] = {\"role\": \"system\", \"content\": followup_system_prompt}\n",
    "\n",
    "        response = main_model(\n",
    "            model=model_choice,\n",
    "            messages=messages,\n",
    "            tools=tools\n",
    "        )\n",
    "\n",
    "        print(f\"After tool call - finish_reason: {response.choices[0].finish_reason}\")\n",
    "    \n",
    "    reply = response.choices[0].message.content\n",
    "        \n",
    "    if generated_code:\n",
    "        try:\n",
    "            print(f\"Generating image for code: {generated_code}\")\n",
    "            image = generate_robot_image(generated_code)\n",
    "        except Exception as e:\n",
    "            print(f\"Image generation failed: {e}\")\n",
    "    \n",
    "    try:\n",
    "        audio = text_to_speech(reply)\n",
    "    except Exception as e:\n",
    "        print(f\"Audio generation failed: {e}\")\n",
    "        audio = None\n",
    "    \n",
    "    return reply, audio, image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "392f9300",
   "metadata": {},
   "outputs": [],
   "source": [
    "def process_text_input(message, history, model_choice):\n",
    "    if not message.strip():\n",
    "        return history, None, None\n",
    "    \n",
    "    reply, audio, image = chat(message, history, model_choice)\n",
    "    history.append({\"role\": \"user\", \"content\": message})\n",
    "    history.append({\"role\": \"assistant\", \"content\": reply})\n",
    "\n",
    "    return history, audio, image\n",
    "\n",
    "def process_audio_input(audio_file, history, model_choice):\n",
    "    if audio_file is None:\n",
    "        return history, None, None\n",
    "    \n",
    "    message = transcribe_audio(audio_file)\n",
    "    if not message:\n",
    "        return history, None, None\n",
    "    \n",
    "    return process_text_input(message, history, model_choice)\n",
    "\n",
    "with gr.Blocks(title=\"Technical Q&A Assistant\") as demo:\n",
    "    gr.Markdown(\"# 🤖 Technical Q&A Assistant\")\n",
    "    gr.Markdown(\"Ask technical questions and get explanations with code examples!\")\n",
    "    \n",
    "    with gr.Row():\n",
    "        with gr.Column(scale=3):\n",
    "            chatbot = gr.Chatbot(\n",
    "                height=400,\n",
    "                type=\"messages\",\n",
    "                label=\"Conversation\"\n",
    "            )\n",
    "            \n",
    "            with gr.Row():\n",
    "                text_input = gr.Textbox(\n",
    "                    placeholder=\"Ask a technical question...\",\n",
    "                    label=\"Text Input\",\n",
    "                    scale=4\n",
    "                )\n",
    "                text_submit = gr.Button(\"Send\", scale=1)\n",
    "            \n",
    "            with gr.Row():\n",
    "                audio_input = gr.Audio(\n",
    "                    sources=[\"microphone\"],\n",
    "                    type=\"filepath\",\n",
    "                    label=\"Voice Input\"\n",
    "                )\n",
    "                audio_submit = gr.Button(\"Send Voice\", scale=1)\n",
    "        \n",
    "        with gr.Column(scale=1):\n",
    "            model_selector = gr.Dropdown(\n",
    "                choices=list(MODELS.keys()),\n",
    "                value=\"GPT-5\",\n",
    "                label=\"Choose Model\"\n",
    "            )\n",
    "            \n",
    "            audio_output = gr.Audio(\n",
    "                label=\"Audio Response\",\n",
    "                autoplay=True\n",
    "            )\n",
    "            \n",
    "            image_output = gr.Image(\n",
    "                label=\"Generated Image\",\n",
    "                height=300,\n",
    "                interactive=False\n",
    "            )\n",
    "    \n",
    "    text_submit.click(\n",
    "        process_text_input,\n",
    "        inputs=[text_input, chatbot, model_selector],\n",
    "        outputs=[chatbot, audio_output, image_output]\n",
    "    ).then(\n",
    "        lambda: \"\",\n",
    "        outputs=text_input\n",
    "    )\n",
    "    \n",
    "    audio_submit.click(\n",
    "        process_audio_input,\n",
    "        inputs=[audio_input, chatbot, model_selector],\n",
    "        outputs=[chatbot, audio_output, image_output]\n",
    "    )\n",
    "    \n",
    "    text_input.submit(\n",
    "        process_text_input,\n",
    "        inputs=[text_input, chatbot, model_selector],\n",
    "        outputs=[chatbot, audio_output, image_output]\n",
    "    ).then(\n",
    "        lambda: \"\",  \n",
    "        outputs=text_input\n",
    "    )\n",
    "\n",
    "demo.launch()\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
